﻿/*
 * protocol_mfq.cpp
 *
 *  Created on: 2019年3月9日
 *      Author: turner
 */
#include "protocol_tcp.hpp"
#include <dm/scada/deviceagent.hpp>
#include <dm/os/log/logger.hpp>

#include <dm/protocol/framemodbustcp.hpp>

static const char* logModule = "CProtocolTcp.tools.dm";

typedef dm::protocol::CFrameModbusApdu::EFunc fun_t;

CProtocolTcp::CProtocolTcp( dm::protocol::CModbusMap* modbusMap ):dm::os::protocol::CModbusTcpMaster(),m_debug(false),m_map(modbusMap){
	m_map->regs().curReset();
}

const char* CProtocolTcp::name()const{
	return "DM-ModbusTcp";
}

bool CProtocolTcp::setDev( const char* name ){
	if( m_map==NULL ){
		log().warnning(THISMODULE "未指定映射表");
		return false;
	}

	if( m_devAgent==NULL )
		m_devAgent = new dm::scada::CDeviceAgent;
	dm::scada::CDeviceAgent::SSignalsSize size;
	size.status = m_map->statuses().size();
	size.discrete = m_map->discretes().size();
	size.measure = m_map->measures().size();
	size.cumulant = m_map->cumulants().size();
	size.remoteCtl = m_map->rmtctls().size();
	size.parameter = m_map->parameters().size();

	return m_devAgent->setByName(name,size);
}

void CProtocolTcp::reset( const ts_t& ts,const rts_t& rts ){
	dm::os::protocol::CModbusTcpMaster::reset(ts,rts);
	enableRequestMsgCheck();
}

CProtocolTcp::EAction CProtocolTcp::dealRxResponse_readCoils( const dm::protocol::CFrameModbusTcp& rf,dm::protocol::CFrameModbusTcp& tf,const ts_t& ts,const rts_t& rts ){
	dm::uint16 startAddr = tf.getData_uint16(0);
	dm::uint16 quantity = tf.getData_uint16(2);

	if( rf.isErrorUpFrame() ){
		dm::protocol::CFrameModbusTcp::EErrCode errCode;
		rf.getError(errCode);

		log().info(THISMODULE "收到错误帧:%02X",errCode);
	}else{
		if( rf.getData_uint8()!=tf.getBytes(quantity) ){
			log().warnning(THISMODULE "收到数据字节数%d 与请求数量%d->%d不匹配",rf.getData_uint8(),quantity,tf.getBytes(quantity));
			return Action_Nothing;
		}

		m_map->regs().update(dm::protocol::CFrameModbusApdu::ReadCoilStatus,startAddr,quantity,rf.getData()+1,rf.getData_uint8());
	}

	return timedRefresh(ts,rts);
}

CProtocolTcp::EAction CProtocolTcp::dealRxResponse_readDiscreteInputs( const dm::protocol::CFrameModbusTcp& rf,dm::protocol::CFrameModbusTcp& tf,const ts_t& ts,const rts_t& rts ){
	dm::uint16 startAddr = tf.getData_uint16(0);
	dm::uint16 quantity = tf.getData_uint16(2);

	if( rf.isErrorUpFrame() ){
		dm::protocol::CFrameModbusTcp::EErrCode errCode;
		rf.getError(errCode);
		log().info(THISMODULE "收到错误帧:%02X",errCode);
	}else{
		if( rf.getData_uint8()!=tf.getBytes(quantity) ){
			log().warnning(THISMODULE "收到数据字节数%d 与请求数量%d->%d不匹配",rf.getData_uint8(),quantity,tf.getBytes(quantity));
			return Action_Nothing;
		}

		m_map->regs().update(dm::protocol::CFrameModbusApdu::ReadInputStatus,startAddr,quantity,rf.getData()+1,rf.getData_uint8());
	}

	return timedRefresh(ts,rts);
}

CProtocolTcp::EAction CProtocolTcp::dealRxResponse_readHoldingRegisters( const dm::protocol::CFrameModbusTcp& rf,dm::protocol::CFrameModbusTcp& tf,const ts_t& ts,const rts_t& rts ){
	dm::uint16 startAddr = tf.getData_uint16(0);
	dm::uint16 quantity = tf.getData_uint16(2);

	if( rf.isErrorUpFrame() ){
		dm::protocol::CFrameModbusTcp::EErrCode errCode;
		rf.getError(errCode);
		log().info(THISMODULE "收到错误帧:%02X",errCode);
	}else{
		if( rf.getData_uint8()!=(quantity*2) ){
			log().warnning(THISMODULE "收到数据字节数%d 与请求数量%d->%d不匹配",rf.getData_uint8(),quantity,quantity*2);
			return Action_Nothing;
		}

		m_map->regs().update(dm::protocol::CFrameModbusApdu::ReadHoldReg,startAddr,quantity,rf.getData()+1,rf.getData_uint8());
	}

	return timedRefresh(ts,rts);
}

CProtocolTcp::EAction CProtocolTcp::dealRxResponse_readInputRegisters( const dm::protocol::CFrameModbusTcp& rf,dm::protocol::CFrameModbusTcp& tf,const ts_t& ts,const rts_t& rts ){
	dm::uint16 startAddr = tf.getData_uint16(0);
	dm::uint16 quantity = tf.getData_uint16(2);

	if( rf.isErrorUpFrame() ){
		dm::protocol::CFrameModbusTcp::EErrCode errCode;
		rf.getError(errCode);
		log().info(THISMODULE "收到错误帧:%02X",errCode);
	}else{
		if( rf.getData_uint8()!=(quantity*2) ){
			log().warnning(THISMODULE "收到数据字节数%d 与请求数量%d->%d不匹配",rf.getData_uint8(),quantity,quantity*2);
			return Action_Nothing;
		}

		m_map->regs().update(dm::protocol::CFrameModbusApdu::ReadInputReg,startAddr,quantity,rf.getData()+1,rf.getData_uint8());
	}

	return timedRefresh(ts,rts);
}

/**
 * 开始召唤任务
 * @param tf
 * @return
 */
CProtocolTcp::EAction CProtocolTcp::taskStart_call( frame_modbus_t& tf,const ts_t& /*ts*/,const rts_t& /*rts*/ ){
	if( m_map==NULL ){
		log().error(THISMODULE "未指定apdu映射");
		return Action_CloseLink;
	}

	m_map->regs().clearUpdate();

	// 获取召唤命令列表，分步召唤
	m_map->regs().curReset();
	if( m_map->regs().curSegment() ){
		switch( m_map->regs().curFun() ){
		case dm::protocol::CFrameModbusApdu::ReadCoilStatus:
			tf.setReadCoils(m_map->regs().curSegment()->address(),m_map->regs().curSegment()->quantity());
			break;
		case dm::protocol::CFrameModbusApdu::ReadInputStatus:
			tf.setReadDiscreteInputs(m_map->regs().curSegment()->address(),m_map->regs().curSegment()->quantity());
			break;
		case dm::protocol::CFrameModbusApdu::ReadHoldReg:
			tf.setReadHoldingRegisters(m_map->regs().curSegment()->address(),m_map->regs().curSegment()->quantity());
			break;
		case dm::protocol::CFrameModbusApdu::ReadInputReg:
			tf.setReadInputRegisters(m_map->regs().curSegment()->address(),m_map->regs().curSegment()->quantity());
			break;
		default:
			log().warnning(THISMODULE "未实现的功能码%d",m_map->regs().curFun());
			m_taskCall = Success;
			return Action_ReTime;
		}

		m_taskCall = Started;
		return Action_SendAndReTime;
	}else{
		log().debug(THISMODULE "未指定召唤");
		m_taskCall = Success;
		return Action_ReTime;
	}
}

CProtocolTcp::EAction CProtocolTcp::taskDo_call( frame_modbus_t& tf,const ts_t& ts,const rts_t& rts ){
	if( m_map->regs().curNext() ){
		switch( m_map->regs().curFun() ){
		case dm::protocol::CFrameModbusApdu::ReadCoilStatus:
			tf.setReadCoils(m_map->regs().curSegment()->address(),m_map->regs().curSegment()->quantity());
			break;
		case dm::protocol::CFrameModbusApdu::ReadInputStatus:
			tf.setReadDiscreteInputs(m_map->regs().curSegment()->address(),m_map->regs().curSegment()->quantity());
			break;
		case dm::protocol::CFrameModbusApdu::ReadHoldReg:
			tf.setReadHoldingRegisters(m_map->regs().curSegment()->address(),m_map->regs().curSegment()->quantity());
			break;
		case dm::protocol::CFrameModbusApdu::ReadInputReg:
			tf.setReadInputRegisters(m_map->regs().curSegment()->address(),m_map->regs().curSegment()->quantity());
			break;
		default:
			log().warnning(THISMODULE "未实现的功能码%d",m_map->regs().curFun());
			m_taskCall = Success;
			return Action_ReTime;
		}

		m_taskCall = Started;
		m_tsCall = rts;
		return Action_SendAndReTime;
	}else{
		for( unsigned int i=0;i<m_map->statuses().size();++i ){
			if( m_map->isUpdatedStatus(i) )
				m_devAgent->updateStatus(i,m_map->valueStatus(i,m_debug),dm::scada::ByCollector,ts);
		}

		for( unsigned int i=0;i<m_map->discretes().size();++i ){
			if( m_map->isUpdatedDiscrete(i) )
				m_devAgent->updateDiscrete(i,m_map->valueDiscrete(i,m_debug),dm::scada::ByCollector,ts);
		}

		for( unsigned int i=0;i<m_map->measures().size();++i ){
			if( m_map->isUpdatedMeasure(i) )
				m_devAgent->updateMeasure(i,m_map->valueMeasure(i,m_debug),dm::scada::ByCollector,ts);
		}

		for( unsigned int i=0;i<m_map->cumulants().size();++i ){
			if( m_map->isUpdatedCumulant(i) )
				m_devAgent->updateCumulant(i,m_map->valueCumulant(i,m_debug),dm::scada::ByCollector,ts);
		}

		endTask_call(ts,rts,Success);
		return Action_ReTime;
	}
}

CProtocolTcp::EAction CProtocolTcp::taskStart_remoteControl( frame_modbus_t& tf,const ts_t& /*ts*/,const rts_t& /*rts*/ ){
	int idx = m_devAgent->getLocalRemoteControlIndex(m_rmtctlIndex);
	const dm::protocol::CModbusMapRmtctl* rmtctl = m_map->rmtctls()[idx];

	dm::uint16 r;
	switch( rmtctl->fun() ){
	case dm::protocol::CFrameModbusApdu::ForceSigCoil:
		if( m_rmtctl==dm::msg::Rc_Open ){
			tf.setWriteSingleCoil(rmtctl->address(),false);
		}else if(m_rmtctl==dm::msg::Rc_Close ){
			tf.setWriteSingleCoil(rmtctl->address(),true);
		}else{
			log().warnning(THISMODULE "不支持的远控命令%d",m_rmtctl);
			m_taskRmtCtl = Permit;
			return Action_Nothing;
		}

		break;
	case dm::protocol::CFrameModbusApdu::PresetSigReg:
		r = m_map->regs().get(rmtctl->refFun(),rmtctl->refAddress());

		if( m_rmtctl==dm::msg::Rc_Open ){
			r = m_map->regs().combine(rmtctl->bitOffset(),rmtctl->bitLen(),rmtctl->valueOpen(),r);
		}else if( m_rmtctl==dm::msg::Rc_Close ){
			r = m_map->regs().combine(rmtctl->bitOffset(),rmtctl->bitLen(),rmtctl->valueClose(),r);
		}else if( m_rmtctl==dm::msg::Rc_PreOpen ){
			r = m_map->regs().combine(rmtctl->bitOffset(),rmtctl->bitLen(),rmtctl->valuePreOpen(),r);
		}else if( m_rmtctl==dm::msg::Rc_PreClose ){
			r = m_map->regs().combine(rmtctl->bitOffset(),rmtctl->bitLen(),rmtctl->valuePreClose(),r);
		}else{
			log().warnning(THISMODULE "不支持的远控命令%d",m_rmtctl);
			m_taskRmtCtl = Permit;
			return Action_Nothing;
		}

		tf.setWriteSingleRegister(rmtctl->address(),r);
		break;
	default:
		log().warnning(THISMODULE "未知远控%d",m_rmtctlIndex);
		m_taskRmtCtl = Fail;
		return Action_Nothing;
	}

	m_taskRmtCtl = Success;
	return Action_SendAndReTime;
}

template<typename T>
static T getParaValue( const dm::msg::SParaData& p ){
	switch( p.type ){
	case dm::msg::SParaData::DtInt8:
		return p.as_int8()[0];
	case dm::msg::SParaData::DtUint8:
		return p.as_uint8()[0];
	case dm::msg::SParaData::DtInt16:
		return p.as_int16()[0];
	case dm::msg::SParaData::DtUint16:
		return p.as_uint16()[0];
	case dm::msg::SParaData::DtInt32:
		return p.as_int32()[0];
	case dm::msg::SParaData::DtUint32:
		return p.as_uint32()[0];
	case dm::msg::SParaData::DtInt64:
		return p.as_int64()[0];
	case dm::msg::SParaData::DtUint64:
		return p.as_uint64()[0];
	case dm::msg::SParaData::DtFloat32:
		return p.as_float32()[0];
	case dm::msg::SParaData::DtFloat64:
		return p.as_float64()[0];
	default:
		return 0;
	}
}

CProtocolTcp::EAction CProtocolTcp::taskStart_parameters( frame_modbus_t& tf,const ts_t& /*ts*/,const rts_t& /*rts*/ ){
	if( !m_paraSet ){
		log().warnning(THISMODULE "不支持读取指令");
		m_taskParas = Permit;
		return Action_Nothing;
	}

	dm::uint8 r[8];
	dm::uint16 q;

	int idx = m_devAgent->getLocalParamterIndex(m_paraData.index);
	const dm::protocol::CModbusMapParameter* param = m_map->parameters()[idx];
	switch( param->fun() ){
	case dm::protocol::CFrameModbusApdu::PresetSigReg:
		if( param->type()==param->DtInt16 ){
			m_map->regs().fromInt16(getParaValue<dm::int16>(m_paraData),r);
		}else if( param->type()==param->DtUint16 ){
			m_map->regs().fromUint16(getParaValue<dm::uint16>(m_paraData),r);
		}else if( param->type()==param->DtBcdUint16 ){
			m_map->regs().fromBcdUint16(getParaValue<dm::uint16>(m_paraData),r);
		}else{
			log().warnning(THISMODULE "参数%d 06指令不支持该类型%d",idx,param->type());
			m_taskParas = Fail;
			return Action_Nothing;
		}

		tf.setWriteSingleRegister(param->address(),r[0]);
		break;
	case dm::protocol::CFrameModbusApdu::PresetMtlReg:
		if( param->type()==param->DtInt16 ){
			q = 1;
			m_map->regs().fromInt16(getParaValue<dm::int16>(m_paraData),r);
		}else if( param->type()==param->DtUint16 ){
			q = 1;
			m_map->regs().fromUint16(getParaValue<dm::uint16>(m_paraData),r);
		}else if( param->type()==param->DtInt32 ){
			q = 2;
			m_map->regs().fromInt32(getParaValue<dm::int32>(m_paraData),r);
		}else if( param->type()==param->DtInt32Inv ){
			q = 2;
			m_map->regs().fromInt32Inv(getParaValue<dm::int32>(m_paraData),r);
		}else if( param->type()==param->DtUint32 ){
			q = 2;
			m_map->regs().fromUint32(getParaValue<dm::uint32>(m_paraData),r);
		}else if( param->type()==param->DtUint32Inv ){
			q = 2;
			m_map->regs().fromUint32Inv(getParaValue<dm::uint32>(m_paraData),r);
		}else if( param->type()==param->DtInt64 ){
			q = 4;
			m_map->regs().fromInt64(getParaValue<dm::int64>(m_paraData),r);
		}else if( param->type()==param->DtInt64Inv ){
			q = 4;
			m_map->regs().fromInt64Inv(getParaValue<dm::int64>(m_paraData),r);
		}else if( param->type()==param->DtUint64 ){
			q = 4;
			m_map->regs().fromUint64(getParaValue<dm::uint64>(m_paraData),r);
		}else if( param->type()==param->DtUint64Inv ){
			q = 4;
			m_map->regs().fromUint64Inv(getParaValue<dm::uint64>(m_paraData),r);
		}else if( param->type()==param->DtFloat32 ){
			q = 2;
			m_map->regs().fromFloat32(getParaValue<dm::float32>(m_paraData),r);
		}else if( param->type()==param->DtFloat32Inv ){
			q = 2;
			m_map->regs().fromFloat32Inv(getParaValue<dm::float32>(m_paraData),r);
		}else if( param->type()==param->DtFloat64 ){
			q = 4;
			m_map->regs().fromFloat64(getParaValue<dm::float64>(m_paraData),r);
		}else if( param->type()==param->DtFloat64Inv ){
			q = 4;
			m_map->regs().fromFloat64Inv(getParaValue<dm::float64>(m_paraData),r);
		}else if( param->type()==param->DtBcdUint16 ){
			q = 1;
			m_map->regs().fromBcdUint16(getParaValue<dm::uint16>(m_paraData),r);
		}else if( param->type()==param->DtBcdUint32 ){
			q = 2;
			m_map->regs().fromBcdUint32(getParaValue<dm::uint32>(m_paraData),r);
		}else if( param->type()==param->DtBcdUint32Inv ){
			q = 2;
			m_map->regs().fromBcdUint32Inv(getParaValue<dm::uint32>(m_paraData),r);
		}else if( param->type()==param->DtBcdUint64 ){
			q = 4;
			m_map->regs().fromBcdUint64(getParaValue<dm::uint64>(m_paraData),r);
		}else if( param->type()==param->DtBcdUint64Inv ){
			q = 4;
			m_map->regs().fromBcdUint64Inv(getParaValue<dm::uint64>(m_paraData),r);
		}else{
			log().warnning(THISMODULE "参数%d 16指令不支持该类型%d",idx,param->type());
			m_taskParas = Fail;
			return Action_Nothing;
		}

		tf.setWriteMultipleRegisters(param->address(),q,r);

		break;
	default:
		log().warnning(THISMODULE "参数%d 指令不支持%d",idx,param->fun());
		m_taskParas = Fail;
		return Action_ReTime;
	}

	m_taskParas = Success;
	return Action_SendAndReTime;
}

CProtocolTcp::EAction CProtocolTcp::task_none( const ts_t& ts,const rts_t& rts ){
	setTask_call(ts,rts);
	return Action_ReTime;
}
